import React from "react";
import { useForm } from "@formily/react";
import { Spin } from "antd";
import { Select } from "@formily/antd";
import { SelectProps } from "antd/es/select";
import debounce from "lodash/debounce";
import { SelectOption } from "src/utils/interface";
import api from "src/http/api";
import { getOptions } from "src/utils/index";

interface DebounceSelectProps<ValueType = any>
  extends Omit<SelectProps<ValueType>, "options" | "children"> {
  fetchOptions: (search: string) => Promise<ValueType[]>;
  debounceTimeout?: number;
}

function DebounceSelect<
  ValueType extends {
    key?: string;
    label: React.ReactNode;
    value: string | number;
  } = any
>({ fetchOptions, debounceTimeout, ...props }: DebounceSelectProps) {
  const [fetching, setFetching] = React.useState(false);
  const [options, setOptions] = React.useState<ValueType[]>([]);
  const fetchRef = React.useRef(0);

  const debounceFetcher = React.useMemo(() => {
    const loadOptions = (value: string) => {
      fetchRef.current += 1;
      const fetchId = fetchRef.current;
      setOptions([]);
      setFetching(true);

      fetchOptions(value).then((newOptions) => {
        if (fetchId !== fetchRef.current) {
          return;
        }

        setOptions(newOptions);
        setFetching(false);
      });
    };

    return debounce(loadOptions, debounceTimeout);
  }, [fetchOptions, debounceTimeout]);

  return (
    <Select
      showSearch
      labelInValue
      filterOption={false}
      onSearch={debounceFetcher}
      notFoundContent={fetching ? <Spin size="small" /> : null}
      {...props}
      options={options}
    />
  );
}

interface Props {
  debounceTimeout?: number;
  placeholder?: string;
  style?: React.CSSProperties;
  value?: string;
  onChange: (value: any) => void;
}

const FetchSelect: React.FC<Props> = (props) => {
  const initValue = props.value ? getOptions([props.value]) : [];

  const [value, setValue] = React.useState<SelectOption[]>(initValue);
  const form = useForm();

  const fetchStorageUriList = async (
    query: string
  ): Promise<SelectOption[]> => {
    const type = form.values.type;
    return new Promise((resolve) => {
      api.getStorageUri({ query, type }).then((res: any) => {
        resolve(getOptions(res.data));
      });
    });
  };

  return (
    <DebounceSelect
      {...props}
      value={value}
      placeholder={props.placeholder || "Select StorageUri"}
      debounceTimeout={props.debounceTimeout || 800}
      fetchOptions={fetchStorageUriList}
      onChange={(newValue) => {
        props.onChange(newValue.value);
        setValue(newValue);
      }}
      style={props.style || { width: "100%" }}
    />
  );
};

export default FetchSelect;
